#include "BASEHACKREGION.h"

BASEHACKREGION::BASEHACKREGION(std::vector<std::pair<int, int>> region) {
    std::vector<cv::Point> temp;
    for (int i = 0; i < region.size(); i++) {
        cv::Point point = {region[i].first, region[i].second};
        temp.push_back(point);
    }
    this->hack_region = temp;
    this->hack_region.push_back(temp[0]);  // 将起始点放入区域最后一位，形成闭环
    this->sendflag = false;
    this->region_start_alert_time = Get_Current_Timestamp();
    this->region_last_squeeze_flag = false;
    this->region_last_sendalret_time = 0;
}

BASEHACKREGION::BASEHACKREGION(int region[][2], int pointNums) {
    std::vector<cv::Point> temp;
    for (int i = 0; i < pointNums; i++) {
        cv::Point point = {region[i][0], region[i][1]};
        temp.push_back(point);
    }
    this->hack_region = temp;
    this->hack_region.push_back(temp[0]);  // 将起始点放入区域最后一位，形成闭环
    this->sendflag = false;
    this->region_start_alert_time = Get_Current_Timestamp();
    this->region_last_squeeze_flag = false;
    this->region_last_sendalret_time = 0;
}
BASEHACKREGION::BASEHACKREGION(std::vector<cv::Point> region) {
    this->hack_region = region;
    this->hack_region.push_back(region[0]);  // 将起始点放入区域最后一位，形成闭环
    this->sendflag = false;
    this->region_start_alert_time = Get_Current_Timestamp();
    this->region_last_squeeze_flag = false;
    this->region_last_sendalret_time = 0;
}
BASEHACKREGION::BASEHACKREGION() {
    this->sendflag = false;
    this->region_start_alert_time = Get_Current_Timestamp();
    this->region_last_squeeze_flag = false;
    this->region_last_sendalret_time = 0;
}
// 判断点在线段上
// (px0, py0)	:	点坐标
// (px1, py1)	:	边的第一个点
// (px2,py2)	:	边的第二个点
bool BASEHACKREGION::IsPointOnLine(float px0, float py0, float px1, float py1, float px2, float py2) {
    bool flag = false;
    float d1 = (px1 - px0) * (py2 - py0) - (px2 - px0) * (py1 - py0);
    if ((abs(d1) < 0.000001) && ((px0 - px1) * (px0 - px2) <= 0) && ((py0 - py1) * (py0 - py2) <= 0)) {
        flag = true;
    }
    return flag;
}

// 判断两线段相交
// (px1, py1)	:	第一边的第一个点
// (px2, py2)	:	第一边的第二个点
// (px3, py3)	:	第二边的第一个点
// (px4, py4)	:	第二边的第二个点
bool BASEHACKREGION::IsIntersect(float px1, float py1, float px2, float py2, float px3, float py3, float px4, float py4) {
    bool flag = false;
    float d = (px2 - px1) * (py4 - py3) - (py2 - py1) * (px4 - px3);
    if (d != 0) {
        float r = ((py1 - py3) * (px4 - px3) - (px1 - px3) * (py4 - py3)) / d;
        float s = ((py1 - py3) * (px2 - px1) - (px1 - px3) * (py2 - py1)) / d;
        if ((r >= 0) && (r <= 1) && (s >= 0) && (s <= 1)) {
            flag = true;
        }
    }
    return flag;
}

// 判断点是否在多边形内(点在多边形的边上也算在内部)
bool BASEHACKREGION::IsPointInPolygon(cv::Point targetPoint) {
    bool isInside = false;
    int count = 0;
    // 求出多边形的最小X
    int minX = INT_MAX;
    for (int i = 0; i < this->hack_region.size(); i++) {
        minX = std::min(minX, this->hack_region[i].x);
    }

    float px = targetPoint.x;
    float py = targetPoint.y;

    // 负X方向的水平射线，(x,y)做起点，(minX, y)做终点
    float linePoint1x = targetPoint.x;
    float linePoint1y = targetPoint.y;
    float linePoint2x = minX - 10;
    float linePoint2y = targetPoint.x;

    // 遍历每一条边
    for (int i = 0; i < this->hack_region.size() - 1; i++) {
        float cx1 = this->hack_region[i].x;  // 多边形的第i个点
        float cy1 = this->hack_region[i].y;
        float cx2 = this->hack_region[i + 1].x;  // 多边形的第i+1个点
        float cy2 = this->hack_region[i + 1].y;

        // 点在多边形上，算是在内部
        if (IsPointOnLine(px, py, cx1, cy1, cx2, cy2)) {
            return true;
        }

        // X方向水平的边，不用计算，肯定不会和射线相交
        if (fabs(cy2 - cy1) < 0.000001) {
            continue;
        }

        // 多边形的一个顶点在射线上，且该顶点是上顶点(y值较高)，算一个交点
        if (IsPointOnLine(cx1, cy1, linePoint1x, linePoint1y, linePoint2x, linePoint2y)) {
            if (cy1 > cy2) {
                count++;
            }
        }
        // 多边形的一个顶点在射线上，且该顶点是上顶点(y值较高)，算一个交点
        else if (IsPointOnLine(cx2, cy2, linePoint1x, linePoint1y, linePoint2x, linePoint2y)) {
            if (cy2 > cy1) {
                count++;
            }
        }
        // 已经排除平行的情况，其他相交的都算一个交点
        else if (IsIntersect(cx1, cy1, cx2, cy2, linePoint1x, linePoint1y, linePoint2x, linePoint2y)) {
            count++;
        }
    }

    // 交点数为奇数，则在多边形内，反之在多边形外
    if (count % 2 == 1) {
        isInside = true;
    }

    return isInside;
}

bool BASEHACKREGION::detectHack(cv::Point targetPoint) {
    return IsPointInPolygon(targetPoint);
}

void BASEHACKREGION::showRegion(cv::Mat& image) {
    for (size_t i = 0; i < this->hack_region.size() - 1; ++i) {
        int x1 = int(this->hack_region[i].x);
        int y1 = int(this->hack_region[i].y);
        int x2 = int(this->hack_region[i + 1].x);
        int y2 = int(this->hack_region[i + 1].y);
        cv::line(image, cv::Point(x1, y1), cv::Point(x2, y2), cv::Scalar(0, 0, 255), 3);
    }
    static const std::string kWinName = "region in image";
    cv::namedWindow(kWinName, cv::WINDOW_NORMAL);

    cv::imshow(kWinName, image);
    cv::waitKey(50);
}

void BASEHACKREGION::drawRegion(cv::Mat& image) {
    for (size_t i = 0; i < this->hack_region.size() - 1; ++i) {
        int x1 = int(this->hack_region[i].x);
        int y1 = int(this->hack_region[i].y);
        int x2 = int(this->hack_region[i + 1].x);
        int y2 = int(this->hack_region[i + 1].y);
        cv::line(image, cv::Point(x1, y1), cv::Point(x2, y2), cv::Scalar(0, 0, 255), 3);
    }
}

void BASEHACKREGION::showRegion(cv::Mat& image, int width, int height) {
    for (size_t i = 0; i < this->hack_region.size() - 1; ++i) {
        int x1 = int(this->hack_region[i].x);
        int y1 = int(this->hack_region[i].y);
        int x2 = int(this->hack_region[i + 1].x);
        int y2 = int(this->hack_region[i + 1].y);
        cv::line(image, cv::Point(x1, y1), cv::Point(x2, y2), cv::Scalar(0, 0, 255), 3);
    }
    static const std::string kWinName = "region in image";
    cv::namedWindow(kWinName, cv::WINDOW_NORMAL);

    cv::resizeWindow(kWinName, width, height);

    cv::imshow(kWinName, image);
    cv::waitKey(50);
}

long long BASEHACKREGION::Get_Current_Timestamp() {
    return std::chrono::duration_cast<std::chrono::milliseconds>(
               std::chrono::system_clock::now().time_since_epoch())
        .count();
}
